#include "RobotAntiEntropy.h"
#include "stage.h"

#include <sstream>

using namespace std;

RobotAntiEntropy::RobotAntiEntropy(Model *model):RobotBase(model)
{
    srand(time(0));
    if(this->id == 0)
        this->entropy = 1;
    else
        this->entropy = 0;
    this->lastTime = GetTime();
    this->entropyId = 0;

    ostringstream out;
    out<<"Entropy is initalized to "<<this->entropy;
    Log(out.str());
}

RobotAntiEntropy::~RobotAntiEntropy()
{
}

void RobotAntiEntropy::Estimate()
{
    BroadCast();
    RemoveDeadNeighbor();
    int nid = RandomSelectNeighbor();
    if(nid != -1)
        ExchangeEntropy(nid);

    if(GetTime() - lastTime >= 20000000)
    {
        if(this->id == 0)
            this->entropy = 1;
        else
            this->entropy = 0;
        this->entropyId++;
        lastTime = GetTime();
        ostringstream out;
        out<<"Entropy is initalized to "<<this->entropy;
        Log(out.str());
    }
    
    float temp = (1.0 / this->entropy);
    if(temp > 0 && temp < 120)
        this->GroupSize = temp;
    if(this->entropy == 0)
        this->GroupSize = 1;
}

void RobotAntiEntropy::MessageReceive(WifiMessageBase* mesg)
{
    //while(this->processMsg);
    //processMsg = true;

    WifiMessageEntropy *msg = dynamic_cast<WifiMessageEntropy*>(mesg);
    float deltaEntropy = 0;
    ostringstream out;

    if(msg == NULL)
    {
        //processMsg = false;
        return;
    }

    switch(msg->type)
    {
        case BroadcastMessage:
            UpdateEntropyList(msg->sourceId);
            break;
        case ExchangeMessage:
            if(msg->targetId != this->id || msg->entropyId != this->entropyId)
            {
                //processMsg = false;
                return;
            }

            if(GetSession(msg->sourceId) == SESSION_OPEN)
            {
                deltaEntropy = 0;
            }
            else
            {
                /*if(msg->historyEntropy != -GetHistoryEntropy(msg->sourceId))
                {
                    this->entropy -= GetHistoryEntropy(msg->sourceId);
                    deltaEntropy = msg->historyEntropy;
                    SetHistoryEntropy(msg->sourceId, GetHistoryEntropy(msg->sourceId));
                    SetLastEntropy(msg->sourceId, deltaEntropy);
                }
                else
                {*/
                    deltaEntropy = (this->entropy + msg->sourceEntropy) / 2 - this->entropy;
                    this->entropy = this->entropy + deltaEntropy;
                    //UpdateEntropyList(msg->sourceId, deltaEntropy);
                //}
            }
            deltaEntropy = - ( deltaEntropy - 0.1);
            SendAckMessage(msg->sourceId, deltaEntropy, msg->sessionId);
            break;
        case AckMessage:
            if(msg->targetId != this->id || msg->entropyId != this->entropyId)
            {
                //processMsg = false;
                return;
            }

            //UpdateEntropyList(msg->sourceId, msg->deltaEntropy);
            this->entropy += msg->deltaEntropy;
            SetSession(msg->sourceId , SESSION_CLOSED);
            break;
    }
    //processMsg = false;
    return;
}

void RobotAntiEntropy::UpdateEntropyList(unsigned int id, float deltaEntropy)
{
    bool isExsit = false;

    for(int i = 0; i < neighborList.size(); i++)
    {
        if(neighborList[i].neighborId == id)
        {
            neighborList[i].lastUpdate = MAX_UPDATE_TIME;
            neighborList[i].historyEntropy += deltaEntropy;
            neighborList[i].lastEntropy = deltaEntropy;
            isExsit = true;
            break;
        }
    }
    if(!isExsit)
    {
        NeighborNode *n = new NeighborNode();
        n->neighborId = id;
        n->lastUpdate = MAX_UPDATE_TIME;
        n->historyEntropy = deltaEntropy;
        n->lastEntropy = 0;
        n->session = SESSION_CLOSED;
        neighborList.push_back(*n);
    }
}

float RobotAntiEntropy::GetLastEntropy(unsigned int id)
{
    for(int i = 0; i < neighborList.size(); i++)
        if(neighborList[i].neighborId == id)
            return neighborList[i].lastEntropy;
    Log("====Should not come here", ErrorLog);
    return 0;
}

void RobotAntiEntropy::SetLastEntropy(unsigned int id, float entropy)
{
    for(int i = 0; i < neighborList.size(); i++)
    {
        if(neighborList[i].neighborId == id)
        {
            neighborList[i].lastEntropy = entropy;
            break;
        }
    }
}

int RobotAntiEntropy::GetSession(unsigned int id)
{
    for(int i = 0; i < neighborList.size(); i++)
        if(neighborList[i].neighborId == id)
            return neighborList[i].session;
    Log("====Should not come here", ErrorLog);
    return 0;
}

float RobotAntiEntropy::GetHistoryEntropy(unsigned int id)
{
    for(int i = 0; i < neighborList.size(); i++)
        if(neighborList[i].neighborId == id)
            return neighborList[i].historyEntropy;
    Log("====Should not come here", ErrorLog);
    return 0;
}

void RobotAntiEntropy::SetHistoryEntropy(unsigned int id, float entropy)
{
    for(int i = 0; i < neighborList.size(); i++)
    {
        if(neighborList[i].neighborId == id)
        {
            neighborList[i].historyEntropy -= entropy;
            break;
        }
    }
}

void RobotAntiEntropy::SetSession(unsigned int id, int session)
{
    for(int i = 0; i < neighborList.size(); i++)
    {
        if(neighborList[i].neighborId == id)
        {
            neighborList[i].session = session;
            break;
        }
    }
}

void RobotAntiEntropy::BroadCast()
{
    //Log("Broadcast a message", DebugLog);
    WifiMessageEntropy msg;
    msg.type = BroadcastMessage;
    msg.sourceId = id;
    msg.entropyId = this->entropyId;
    Broadcast( static_cast<WifiMessageBase *>(&msg) );
}

int RobotAntiEntropy::RandomSelectNeighbor()
{
    if(neighborList.size() == 0)
        return -1;

    int index = rand()%neighborList.size();
    return neighborList[index].neighborId;
}

void RobotAntiEntropy::RemoveDeadNeighbor()
{
    for(int i = 0; i < neighborList.size(); i++)
    {
        if(--neighborList[i].lastUpdate <= 0)
        {
            // Remove a dead neighbor node
            //this->entropy -= neighborList[i].historyEntropy;
            neighborList.erase(neighborList.begin() + i);
            i--;
        }
    }
}

void RobotAntiEntropy::ExchangeEntropy(unsigned int nid)
{
    ostringstream out;

    WifiMessageEntropy msg;
    msg.type = ExchangeMessage;
    msg.sourceId = id;
    msg.targetId = nid;
    msg.sourceEntropy = this->entropy;
    msg.historyEntropy = GetHistoryEntropy(nid);
    if(GetSession(nid) == SESSION_CLOSED)
    {
        SetSession(nid, SESSION_OPEN);
        msg.sessionId = GetSession(nid);
    }
    else if(GetSession(nid) == SESSION_OPEN)
    {
        msg.sessionId = SESSION_UNCLOSED;
    }
    msg.entropyId = this->entropyId;

    Broadcast( static_cast<WifiMessageBase *>(&msg) );
}

void RobotAntiEntropy::SendAckMessage(unsigned int targetId, float deltaEntropy, unsigned int sessionId)
{
    ostringstream out;
    WifiMessageEntropy msg;
    msg.type = AckMessage;
    msg.sourceId = this->id;
    msg.targetId = targetId;
    msg.deltaEntropy = deltaEntropy;
    msg.sessionId = sessionId;
    msg.entropyId = this->entropyId;

    Broadcast( static_cast<WifiMessageBase *>(&msg) );
}
